package com.wistech.search.solr.extend;

import java.util.List;
import java.util.Map;

import com.google.common.collect.Maps;
import com.wistech.search.solr.score.ScoreRule;
import com.wistech.search.solr.score.XscorerFactory;
import com.wistech.utils.ConverterUtils;
import com.wistech.utils.MathUtils;

/**
 * 评分处理器基类
 * 
 * @author Liuye
 * 
 * @param <T>
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public abstract class ScoreHandler<T> implements DocumentHander {
	private List<ScoreRule> scoreRules;

	@Override
	public boolean add(SolrIndexContext context) {
		calcScore(context);
		return true;
	}

	public void calcScore(SolrIndexContext context) {
		// 根据id查询实体，并实体存在，合并实体的属性到context的model
		String idName = context.getRequest().getSchema().getUniqueKeyField()
				.getName();
		String id = context.getDocument().getFieldValue(idName).toString();
		Map<String, Object> mergeModel = Maps.newHashMap();
		T t = getEntity(id);
		if (t != null) {
			mergeModel = ConverterUtils.beanToMap(t);
			mergeModel.putAll(context.getModel());
			context.setModel(mergeModel);
		}

		// 合并所有属性至env,用于计算业务评分
		Map<String, Object> env = Maps.newHashMap();
		Map<String, Object> model = context.getModel();
		convert2FloatAndPut(env, model);
		Map<String, Object> relationData = getRelationData(model);
		if (relationData != null) {
			convert2FloatAndPut(env, relationData);
		}
		
		//对数据进行处理
		runScoreRule(env);
		
		// 计算score
		final XscorerFactory xFactory = context.getRequest().getCore()
				.getSchema().getXscorerFactory();
		float score = xFactory.getScoreExpression().eval(env);
		// 保存sroe到model
		model.put("score", score);
	}

	/**
	 * 把model里的数据转换成float类型
	 * 
	 * @param env
	 * @param model
	 */
	public void convert2FloatAndPut(Map<String, Object> env,
			Map<String, Object> model) {
		for (Map.Entry<String, Object> entry : model.entrySet()) {
			String name = entry.getKey();
			Object value = entry.getValue();
			if (value instanceof Number) {
				value = ((Number) value).floatValue();
			} else if (value instanceof String
					&& MathUtils.isNumeric(value.toString(), true)) {
				value = Float.valueOf(value.toString());
			}
			env.put(name, value);
		}
	}

	public abstract T getEntity(String id);

	protected Map<String, Object> getRelationData(Map<String, Object> model) {
		return null;
	}

	@Override
	public boolean delete(String id) {
		return true;
	}

	protected void runScoreRule(Map<String, Object> scoreData) {
		if (scoreRules != null) {
			for (ScoreRule rule : scoreRules) {
				rule.apply(scoreData);
			}
		}
	}

	/**
	 * @param scoreRules
	 *            the scoreRules to set
	 */
	public void setScoreRules(List<ScoreRule> scoreRules) {
		this.scoreRules = scoreRules;
	}
}
